#include "stdafx.h"
#include "opendoor.h"

#include <QtCore/QCoreApplication>
#include <QtNetwork>
#include <iostream>
#ifdef _DEBUG
#include <QMessageBox>
#endif // _DEBUG

using namespace std;

unsigned int exchange ( int org );

/* open the door for specific ip and mac address*/
bool openDoor( const QString &ip , const QString &mac, Data::Operaction operaction /*= CmccOpen */ )
{
    QString newMac = mac;
    QString fixedMac = correctMac ( newMac );
	if ( !isIpclientMac ( fixedMac ) )
		return false;

    QUdpSocket s;
    Data data;
    memset ( &data , 0 , sizeof ( Data ) );

	data.ip = exchange ( QHostAddress ( ip ).toIPv4Address () );

	/* fill the mac*/
	memcpy ( &data.mac , fixedMac.toLocal8Bit ( ).constData ( ) , 17 );

	data.operaction = operaction;

	/* ***** the checksum ***** */
	data.checksum = checkSum ( data );

#ifndef _DEBUG
	/* send the message */
	s.writeDatagram ( QByteArray ( (char *)&data , sizeof ( data ) ) ,
					  QHostAddress ( "202.193.160.123" ) ,
					  20015 );
#endif // _DEBUG

#ifdef _DEBUG
	if ( QMessageBox::critical ( 0 ,
		"send Datagram?" ,
		QString ( "Debug:\nDo you want to send the datagram?\n" ) % newMac ,
		QMessageBox::Yes | QMessageBox::No )
		== QMessageBox::Yes )
	{
		s.writeDatagram ( QByteArray ( (char *)&data , sizeof ( data ) ) ,
						  QHostAddress ( "202.193.160.123" ) ,
						  20015 );

		char recvBuff[10240] = { 0 };
		int recved = 0;

		QTime start = QTime::currentTime ();
		do
		{
			ushort port = 5201;
			int size = s.readDatagram ( recvBuff + recved ,
										sizeof (recvBuff)-recved ,
										&QHostAddress ( QHostAddress::Any ) ,
										0 );
			if ( size != -1 )
			{
				recved += size; \
			}
		} while ( start.secsTo ( QTime::currentTime () ) < 1 && recved < 500 );
		QFile f ( "openRecv.dat" );
		if ( f.open ( QFile::WriteOnly ) )
		{
			f.write ( recvBuff , recved );
			f.close ();
		}
	}
#endif // _DEBUG


	return true;
}

/* calculate the ip client's checksum of the data
 * the algorithm is copied from ip client version 4.3 at 0x0048703D*/
int checkSum ( const Data &data )
{
	unsigned char *d = (unsigned char *)&data;

	int checksum = 0x4e67c6a7;
	for ( int i = 0; i < 0x38; i++ )
	{
		if ( checksum > 0 )
		{
			checksum ^= ( checksum >> 2 ) + ( checksum << 5 ) + (d[i]);
		}
		else
		{
			checksum ^= ( ( checksum >> 2 ) | 0xC0000000 ) + ( checksum << 5 ) + ( d[i] );
		}
	}
	return checksum & 0x7FFFFFFF;
}

/* big-ending & little-ending conversion */
unsigned int exchange ( int org )
{
	int exchanged = ( ( org & 0xFF ) << 24 ) |
		( ( org & 0xFF00 ) << 8 ) |
		( ( org & 0xFF0000 ) >> 8 )|
		( ( org & 0xFF000000 ) >> 24 );
	return exchanged;
}

/* check if the mac address is ipclient's style*/
bool isIpclientMac ( const QString &mac )
{
	/* first , the address must be separated by ':' , but not
	 * any others characters */
	auto numbers = mac.split ( ':' );

	/* second , the address must have six bytes */
	if ( numbers.size () != 6 )
		return false;

	/* and, the address must not be 00:00:00:00:00:00
	 * and, all the blocks separated by ':' must be Hexadecimal digits 
	 * and, all the hexadecimal digits must be only a byte long */
	bool allZeros = true;
	for ( int i = 0; i < 6; i++ )
	{
		bool b;
		const QString &thisDigit = numbers[i];

		/* check its length , must be 2*/
		if ( thisDigit.length () != 2 )
			return false;

		/* check if it is all zero or if it isn't a digit*/
		if ( thisDigit.toInt ( &b , 16 ) )
			allZeros = false;
		if ( !b )
			return false;
	}

	return !allZeros;
}

/* this function will correct the mac address to ipclient-style mac address.
 * 
 * The mac addresses style which can be correct are :
 * -------------------------------------------------
 * 101122				->	00:00:00:10:11:22
 * 11-22-33-44-55-66	->	11:22:33:44:55:66
 * */
QString &correctMac ( QString &m )
{
	m.replace ( '-' , "" );
	m.replace ( ':' , "" );
	m.replace ( '(' , "" );
	m.replace ( ')' , "" );
	m = m.left ( 12 );

	if ( m.size ( ) < 12 )
	{
		m.insert ( 0 , QString ( "0" ).repeated ( 12 - m.size ( ) ) );
	}
	for ( int i = 10; i; i -= 2 )
	{
		m.insert ( i , ':' );
	}
	return m;
}
